﻿
// TODO:
// We do not support SMS for now.
// #define SUPPORT_SMS

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Xml;
using UDS.Entity;

namespace UDS.Components
{
    /// <summary>
    /// The exception may occur when sending SMS.
    /// </summary>
    public class SMSException : ApplicationException
    {
        /// <summary>
        /// Get the error code when trying to send SMS.
        /// </summary>
        public int ErrorCode
        {
            get;
            private set;
        }

        internal SMSException(int errorCode)
            : base("发送短消息失败: " + GetErrorMsg(errorCode))
        {
            ErrorCode = errorCode;
        }

        private static string GetErrorMsg(int errorCode)
        {
            switch (errorCode)
            {
                case 0:
                    return "成功";

                case -1:
                    return "串口未打开";

                case -2:
                    return "消息超过长度";

                case -3:
                    return "手机号码不正确";

                case -4:
                    return "发送阵列已满";

                case -8:
                    return "模块未打开";

                default:
                    return string.Format("未知错误 (ERROR CODE 0x{0:x})", errorCode);
            }
        }
    }

    /// <summary>
    /// SMS 
    /// </summary>
    public class SMS
    {
        /*
		#region 引用组件
		[DllImport("../bin/AscendSMS.dll")]
		public static extern int OpenComm(int CommIndex);
		[DllImport("../bin/AscendSMS.dll")]
		public static extern int CloseComm(int CommIndex);
		[DllImport("../bin/AscendSMS.dll")]
		public static extern int ForceCloseComm(int CommIndex);
		[DllImport("../bin/AscendSMS.dll")]
		public static extern int SendMsg(int CommIndex,string Msg,string MobileNo,int Msg_Index,bool Chinese);
		[DllImport("../bin/AscendSMS.dll")]
		public static extern int GetUnSendCount(int CommIndex);
		[DllImport("../bin/AscendSMS.dll")]
		public static extern bool GetNextSendMsg(int CommIndex, string Msg,bool DeleteAfterRead);
		[DllImport("../bin/AscendSMS.dll")]
		public static extern bool GetNewMsg(int CommIndex,  string Msg);
		#endregion
		
	
		/// <summary>
		/// 发送短消息
		/// 返回操作码 0 成功 -2 消息超过长度 -3 手机号码不正确 -4 发送阵列已满 -8 模块未打开 -1 串口未打开
		/// </summary>
		/// <param name="Msg">短消息内容</param>
		/// <param name="MobileNo">目标端手机号码,包括一般号码及特殊号码</param>
		/// <param name="MsgIndex">短消息标志位</param>
		/// <param name="IsZh">是否中文(中文为True,否则为False)</param>
		/// <returns>操作码</returns>
		public int SendMobileMsg(string Msg, string MobileNo, int MsgIndex,bool IsZh) 
		{
			CloseComm(CommIndex);
			OpenComm(CommIndex); 
			String[] MoA = MobileNo.Split(',');
			String[] RtnCodeA = MoA;
			for(int i=0;i<MoA.Length;i++)
			{
				int RtnCode = SendMsg(CommIndex,Msg,MoA[i],MsgIndex,IsZh);
				RtnCodeA[i] = RtnCode.ToString();
			}
			CloseComm(CommIndex);
			if(RtnCodeA[0]=="0")
				return 0;
			else
				return -1;
		}		
		
	    */

        #region Revised

        /// <summary>
        /// 得到新消息总数
        /// </summary>
        /// <returns>返回int</returns>
        public static int GetNewMsgCount(string Username)
        {
            var parameters = new[] {
                Database.MakeInParam("@username", SqlDbType.NVarChar, 50, Username)
            };

            using (var db = new Database())
            {
                using (var dr = db.RunDataReader("dbo.sproc_SMS_GetNewMsgCount_V2", parameters))
                {
                    if (dr.Read())
                    {
                        return dr.GetInt32(0);
                    }

                    return 0;
                }
            }
        }

        /// <summary>
        /// Mark read flag for the specified messages
        /// </summary>
        /// <param name="msgs">
        /// The list of message ids seperated by semicolons (;) or commas (,).
        /// </param>
        /// <param name="username">
        /// The name of the user who read the message.
        /// </param>
        public static bool ReadMsg(string msgs, string username)
        {
            if (!string.IsNullOrEmpty(msgs))
            {
                int[] array = Utility.SplitToIntegerArray(msgs);
                return ReadMsg(array, username);
            }

            return true;
        }

        /// <summary>
        /// Mark read flag for the specified messages
        /// </summary>
        public static bool ReadMsg(IEnumerable<int> msgs, string username)
        {
            /*
             * XML Format
             * <messages>
             *  <m id="" />
             * </messages>
             */
            string xml = Utility.GenerateXML(msgs, "messages", "m", "id");
            var parameters = new[] {
                Database.MakeInParam("@MsgIDS", SqlDbType.Xml, -1, xml),
                Database.MakeInParam("@Username", SqlDbType.NVarChar, 50, username),
            };

            using (var db = new Database())
            {
                db.RunProc("dbo.sproc_SMS_ReadMsg_V2", parameters);
                return true;
            }
        }

        /// <summary>
        /// 更新在线记录表.添加新的在线人员
        /// </summary>
        public static void UpdateOnlineInfo(string Username, string HostAddr, string SessionID)
        {
            var parameters = new[] {
                Database.MakeInParam("@username", SqlDbType.NVarChar, 50, Username),
                Database.MakeInParam("@hostaddr", SqlDbType.NVarChar, 50, HostAddr),
                Database.MakeInParam("@sessionid", SqlDbType.NVarChar, 50, SessionID)
            };

            using (var db = new Database())
            {
                db.RunProc("dbo.sproc_SMS_UpdateOnlineInfo_V2", parameters);
            }
        }

        /// <summary>
        /// 更新活动记录及检测未活动人员
        /// </summary>
        /// <param name="Username">
        /// The name of the currently logged-on user.
        /// </param>
        /// <param name="SessionID">
        /// The unique identifier of an ASP.NET session.
        /// </param>
        /// <param name="ActiveNodeID">
        /// The id of the feature that the current user is visiting.
        /// </param>
        /// <param name="numUsers">
        /// The number of users who are visiting the web site. -1 indicates an invalid request.
        /// </param>
        /// <returns>
        /// A boolean value indicating whether the user has new messages or not.
        /// </returns>
        public static bool CheckUpdate(string Username, string SessionID, int ActiveNodeID, out int numUsers)
        {
            bool NewMsgFlag;
            var parameters = new[] {
                Database.MakeInParam("@username", SqlDbType.NVarChar, 100, Username),
                Database.MakeInParam("@sessionid", SqlDbType.NVarChar, 100, SessionID),
                Database.MakeInParam("@ActiveNodeID", SqlDbType.Int, 4, ActiveNodeID),
                Database.MakeOutParam("@ReturnID", SqlDbType.Int, 4),
                Database.MakeOutParam("@NewMsgFlag", SqlDbType.Bit, 4)
            };

            using (var db = new Database())
            {
                db.RunProc("dbo.sproc_SMS_CheckUpdate_V2", parameters);
                numUsers = (int)parameters[3].Value;

                if (numUsers == -1)
                {
                    // If numUsers is equal to -1, it indicates that the current user should be log off 
                    // since the same account has logged on using other computer.
                    NewMsgFlag = false;
                }
                else
                {
                    NewMsgFlag = (bool)parameters[4].Value;
                }

                return NewMsgFlag;
            }
        }

        #endregion Revised

        /// <summary>
        /// 得到所有在线人员
        /// </summary>
        public SqlDataReader GetOnlinePerson()
        {
            var data = new Database();
            try
            {
                return data.RunDataReader("dbo.sproc_SMS_GetOnlinePerson");
            }
            catch (Exception ex)
            {
                Logger.Log(ex.ToString());
                throw new Exception("读取在线人员数据出错!", ex);
            }
        }

        /// <summary>
        /// 得到聊天记录
        /// </summary>
        public SqlDataReader GetHistory(string Receiver, string Sender)
        {
            var data = new Database();
            var parameters = new[] {
                Database.MakeInParam("@Sender", SqlDbType.NVarChar, 50, Sender),
                Database.MakeInParam("@Receiver", SqlDbType.NVarChar, 50, Receiver)
            };
            try
            {
                return data.RunDataReader("dbo.sproc_SMS_GetHistory", parameters);
            }
            catch (Exception ex)
            {
                Logger.Log(ex.ToString());
                throw new Exception("得到聊天记录出错!", ex);
            }
        }

        /// <summary>
        /// 得到我所接收的所有短讯
        /// </summary>
        public SqlDataReader GetMyReceive(string Username)
        {
            var data = new Database();
            var parameters = new[] {
                Database.MakeInParam("@Username", SqlDbType.NVarChar, 50, Username)
            };
            try
            {
                return data.RunDataReader("dbo.sproc_SMS_GetMyAllMsg", parameters);
            }
            catch (Exception ex)
            {
                Logger.Log(ex.ToString());
                throw new Exception("得到我所接收的所有短讯出错!", ex);
            }
        }

        /// <summary>
        /// 得到我所发送的所有短讯
        /// </summary>
        /// <returns></returns>
        public SqlDataReader GetMySent(string Username)
        {
            var data = new Database();
            var parameters = new[] {
                Database.MakeInParam("@Username", SqlDbType.NVarChar, 50, Username)
            };
            try
            {
                return data.RunDataReader("dbo.sproc_SMS_GetMySent", parameters);
            }
            catch (Exception ex)
            {
                Logger.Log(ex.ToString());
                throw new Exception("得到我所接收的所有短讯出错!", ex);
            }
        }

        /// <summary>
        /// 得到某个人的最新一条消息
        /// </summary>
        public static SMSInfo GetNewLocalMsg(string Username)
        {
            var data = new Database();
            var parameters = new[] {
                Database.MakeInParam("@username", SqlDbType.NVarChar, 50, Username)
            };
            using (var db = new Database())
            {
                using (var dr = db.RunDataReader("dbo.sproc_SMS_GetNewMsg_V3", parameters))
                {
                    if (dr.Read())
                    {
                        return SMSInfo.ReadSMSFromIDataReader(dr);
                    }
                    return null;
                }
            }
        }

        /// <summary>
        /// 发送站内短消息
        /// </summary>
        /// <param name="sender">发送者用户名</param>
        /// <param name="receivers">接受者用户名，可用逗号相隔多人</param>
        /// <param name="msg">短消息内容</param>
        /// <param name="sendtime">短消息发送时间</param>
        /// <returns>无返回值</returns>
        private static void SendLocalMsg(string sender, IEnumerable<string> receivers, string msg, DateTime sendtime)
        {
            /*
             * XML format:
             * <receiver>
             *  <r name="" mobileNo="" />
             * </receiver>
             */
            WriteElement w = delegate(XmlWriter writer, object obj)
            {
                string name = (string)obj;
                if (!string.IsNullOrEmpty(name))
                {
                    writer.WriteStartElement("r");
                    writer.WriteAttributeString("name", name);
                    writer.WriteEndElement();
                }
            };
            string xml = Utility.GenerateXML(receivers, "receiver", w);

            int newmsgid;
            using (var db = new Database())
            {
                db.BeginTransaction();

                // Create a new message first
                SqlParameter[] parameters = new SqlParameter[] {
                    Database.MakeInParam("@sender", SqlDbType.NVarChar, 50, sender),
                    Database.MakeInParam("@content", SqlDbType.NVarChar, 255, msg),
                    Database.MakeInParam("@type", SqlDbType.Int, 4, (int)SMSType.SiteMessage),
                    Database.MakeInParam("@sendtime", SqlDbType.DateTime, 30, sendtime),
                    Database.MakeOutParam("@newmsgid", SqlDbType.Int, 4)
                };
                db.RunProc("dbo.sproc_SMS_SendLocalMsg_V2", parameters);
                newmsgid = (int)parameters[4].Value;

                // 将消息与接收者关联
                parameters = new SqlParameter[] {
                    Database.MakeInParam("@MsgID", SqlDbType.Int, 4, newmsgid),
                    Database.MakeInParam("@ReceiverXml", SqlDbType.Xml, -1, xml),
                    Database.MakeInParam("@Type", SqlDbType.Int, 1, (int)SMSType.SiteMessage)
                };
                db.RunProc("dbo.sproc_SMS_SetMsgReceiver_V2", parameters);

                // Commit the transaction finally
                db.Commit();
            }
        }


#if SUPPORT_SMS
        /// <summary>
        ///  发站用户站内手机短消息
        /// </summary>
        /// <param name="Sender">发送者用户名</param>
        /// <param name="RecipientMobileNo">接受者手机号码，可用逗号相隔多人</param>
        /// <param name="Content">短消息内容</param>
        /// <param name="Sendtime">短消息发送时间</param>
        /// <param name="RepeatTimes">短消息重复发送次数</param>
        /// <param name="RepeatPeriod">重复发送周期，以分钟为单位</param>
        /// <returns>返回执行代码 1 正常 2 错误</returns>
        private static int SaveMobileMsgToBuffer(string Sender, string RecipientMobileNo, string Content, DateTime Sendtime, int RepeatTimes, int RepeatPeriod)
        {
            var data = new Database();

        #region 存消息至数据库，返回MsgID

            SqlParameter[] prams1 = {
                Database.MakeInParam("@Sender", SqlDbType.NVarChar, 50, Sender),
                Database.MakeInParam("@RecipientMobileNo", SqlDbType.NVarChar, 255, RecipientMobileNo),
                Database.MakeInParam("@Content", SqlDbType.NVarChar, 255, Content),
                Database.MakeInParam("@Sendtime", SqlDbType.DateTime, 30, Sendtime),
                Database.MakeInParam("@RepeatTimes", SqlDbType.Int, 4, RepeatTimes),
                Database.MakeInParam("@RepeatPeriod", SqlDbType.Int, 4, RepeatPeriod)
            };
            try
            {
                data.RunProc("dbo.sproc_SMS_SaveMobileMsgToBuffer", prams1);
                return 1;
            }
            catch (Exception ex)
            {
                Logger.Log(ex.ToString());
                throw new Exception("存消息至数据库出错!", ex);
            }

        #endregion
        }
#endif

        /// <summary>
        /// 发送短消息
        /// </summary>
        /// <param name="sender">发送者用户名</param>
        /// <param name="recipients">接受者用户名，可用逗号相隔多人</param>
        public static bool SendLocalMsg(string sender, string[] recipients, string msg)
        {
            if (recipients != null && recipients.Length > 0)
            {
                SendLocalMsg(sender, recipients, msg, DateTime.Now);
            }
            return true;
        }

        /// <summary>
        /// 发送短消息
        /// </summary>
        /// <param name="sender">发送者用户名</param>
        /// <param name="recipients">接受者用户名，可用逗号相隔多人</param>
        /// <param name="msg">短消息内容</param>
        /// <param name="type">短消息类型 1为站内消息 2为手机短讯 3为站外手机(取mobileno)</param>
        /// <param name="sendtime">短消息发送时间</param>
        /// <param name="mobileno">如果是站外用户时用。表示站外用户的手机号</param>
        /// <param name="repeattimes">如果是手机短讯时用。表示此条讯息的重复发送次数</param>
        /// <param name="repeattimes">如果是手机短讯时用。表示此条讯息的重复发送时间间隔(以分钟为单位)</param>
        /// <param name="repeatperiod"></param>
        /// <returns>返回操作结束代码</returns>
        public static bool SendMsg(string sender, string recipients, string msg, SMSType type, DateTime sendtime, string mobileno, int repeattimes, int repeatperiod)
        {
            string[] recipientArray = Utility.SplitArray(recipients);

            switch (type)
            {
                case SMSType.SiteMessage: //站内用户的站内短讯
                    SendLocalMsg(sender, recipientArray, msg, sendtime);
                    return true;

#if SUPPORT_SMS
                case SMSType.LocalSMS: //站内用户的手机短讯
                case SMSType.OutSMS: //站外用户的手机短讯
                    if (type == SMSType.LocalSMS)
                    {
                        mobileno = GetMobileNoByUsername(recipients);
                    }
                    int code = SaveMobileMsgToBuffer(sender, mobileno, msg, sendtime, repeattimes, repeatperiod);
                    if (code != 0)
                    {
                        throw new SMSException(code);
                    }
                    return true;
#endif

                default:
                    throw new NotSupportedException(string.Format("Not supported message type: {0}.", type));
            }
        }

        /// <summary>
        /// Delete an array of messages.
        /// </summary>
        /// <param name="msgIds">The comma-seperated message id list, e.g. 123,124,125.</param>
        public static bool MsgDelete(string msgIds)
        {
            int[] array = Utility.SplitToIntegerArray(msgIds);
            return MsgDelete(array);
        }

        /// <summary>
        /// Delete an array of messages.
        /// </summary>
        public static bool MsgDelete(int[] msgIds)
        {
            if (msgIds != null && msgIds.Length > 0)
            {
                /*
                 * XML format:
                 * <messages>
                 *  <m id="" />
                 * </messages>
                 */
                string xml = Utility.GenerateXML(msgIds, "messages", "m", "id");
                var parameters = new[] {
                    Database.MakeInParam("@msgIds", SqlDbType.Xml, -1, xml)
                };

                using (var db = new Database())
                {
                    db.RunProc("dbo.sproc_SMS_DelMsg_V2", parameters);
                }
            }
            return true;
        }
    }
}